#define __host__
#define __device__
#include <vector_types.h>

#include <algorithm/analytical_model/algorithm/cuda/inter_op/receiver/ImagePlane.h>
#include <algorithm/analytical_model/algorithm/cuda/receiver/ImagePlane.cuh>
#include <utils/Utils.h>

#include <memory>

using namespace solar::cuda::inter_op;

ImagePlane::ImagePlane(cudaStream_t stream)
{
    this->delegate_ptr_ = std::make_shared<solar::cuda::ImagePlane>(stream);
}

auto ImagePlane::intersect(const Float3& orig,
                           const Float3& dir) const -> std::tuple<float, float, float, bool>
{
    auto ptr = std::dynamic_pointer_cast<solar::cuda::ImagePlane>(this->delegate_ptr_);
    return ptr->intersect({orig.x, orig.y, orig.z}, {dir.x, dir.y, dir.z});
}

void ImagePlane::setAxis(const Float3& x, const Float3& y) const
{
    auto ptr = std::dynamic_pointer_cast<solar::cuda::ImagePlane>(this->delegate_ptr_);
    float3 x_copy = {x.x, x.y, x.z};
    float3 y_copy = {y.x, y.y, y.z};
    ptr->setAxis(x_copy, y_copy);
}

void ImagePlane::init(float pixel_per_meter_for_receiver) const
{
    this->delegate_ptr_->init(pixel_per_meter_for_receiver);
}

void ImagePlane::setResolution(float pixel_per_meter_for_receiver) const
{
    this->delegate_ptr_->setResolution(pixel_per_meter_for_receiver);
}

auto ImagePlane::getFocusCenter(const Float3& heliostat_position) const -> Float3
{
    float3 heliostat_position_copy = {heliostat_position.x, heliostat_position.y,
                                      heliostat_position.z};
    auto result = this->delegate_ptr_->getFocusCenter(heliostat_position_copy);
    return {result.x, result.y, result.z};
}

auto ImagePlane::getVertices() const -> std::array<Float3, 4>
{
    auto derived_ptr = std::dynamic_pointer_cast<solar::cuda::ImagePlane>(this->delegate_ptr_);
    return {structCopy<Float3>(derived_ptr->rect_vertices_[0]),
            structCopy<Float3>(derived_ptr->rect_vertices_[1]),
            structCopy<Float3>(derived_ptr->rect_vertices_[2]),
            structCopy<Float3>(derived_ptr->rect_vertices_[3])};
}

void ImagePlane::setVertices(const std::array<Float3, 4>& vertices)
{
    auto derived_ptr = std::dynamic_pointer_cast<solar::cuda::ImagePlane>(this->delegate_ptr_);
    std::transform(vertices.begin(), vertices.end(), derived_ptr->rect_vertices_,
                   [&](const Float3& current_vertex)
                   { return structCopy<float3>(current_vertex); });
}
